﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
//using MvcPatch;
using System.Web.Mvc;

namespace MvcShop.Extensions
{
    public static class UrlHelperExtensions
    {
        public static ActionOf<TController> Of<TController>(this UrlHelper helper) where TController : Controller
        {
            return new ActionOf<TController>(helper);
        }

        

        //public static string ToHome(this UrlHelper helper, User user)
        //{
        //    return helper.To<HomeController>().Action(c => c.Index, user);
        //}
    }

    public class ActionOf<TController> where TController : Controller
    {
        public ActionOf(UrlHelper urlHelper)
        {
            this.UrlHelper = urlHelper;
        }

        public UrlHelper UrlHelper { get; private set; }

        //private static TController prototype = new TController();

        //public string Action<T1, T2>(Func<TController, Func<T1, T2, ActionResult>> action, T1 arg1, T2 arg2)
        //{
        //    return Action(action(prototype).Method, arg1, arg2);
        //}

        public string Action(MethodInfo action, object[] args, object routeValues)
        {
            var pathBuilder = VirtualPathBuilder.Create(this.UrlHelper.RequestContext, action, args, routeValues);
            var path = pathBuilder.GetVirtualPath(this.UrlHelper.RouteCollection, this.UrlHelper.RequestContext);

            if (path == null)
            {
                string message = String.Format("Url generation failed for {0}", action);
                throw new ArgumentException(message);
            }

            return path.VirtualPath;
        }

        #region no arguments

        public string Action(Func<TController, Func<ActionResult>> action)
        {
            return Action(action, null);
        }

        public string Action(Func<TController, Func<ActionResult>> action, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[0], routeValues);
        }

        public string Action(Func<TController, Action> action)
        {
            return Action(action, null);
        }

        public string Action(Func<TController, Action> action, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[0], routeValues);
        }

        #endregion

        #region 1 argument

        public string Action<T>(Func<TController, Func<T, ActionResult>> action, T arg)
        {
            return Action(action, arg, null);
        }

        public string Action<T>(Func<TController, Func<T, ActionResult>> action, T arg, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg }, routeValues);
        }

        public string Action<T>(Func<TController, Action<T>> action, T arg)
        {
            return Action(action, arg, null);
        }

        public string Action<T>(Func<TController, Action<T>> action, T arg, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg }, routeValues);
        }

        #endregion

        #region 2 arguments

        public string Action<T1, T2>(Func<TController, Func<T1, T2, ActionResult>> action, T1 arg1, T2 arg2)
        {
            return Action(action, arg1, arg2, null);
        }

        public string Action<T1, T2>(Func<TController, Func<T1, T2, ActionResult>> action, T1 arg1, T2 arg2, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2 }, routeValues);
        }

        public string Action<T1, T2>(Func<TController, Action<T1, T2>> action, T1 arg1, T2 arg2)
        {
            return Action(action, arg1, arg2, null);
        }

        public string Action<T1, T2>(Func<TController, Action<T1, T2>> action, T1 arg1, T2 arg2, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2 }, routeValues);
        }

        #endregion

        #region 3 arguments

        public string Action<T1, T2, T3>(Func<TController, Func<T1, T2, T3, ActionResult>> action, T1 arg1, T2 arg2, T3 arg3)
        {
            return Action(action, arg1, arg2, arg3, null);
        }

        public string Action<T1, T2, T3>(Func<TController, Func<T1, T2, T3, ActionResult>> action, T1 arg1, T2 arg2, T3 arg3, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3 }, routeValues);
        }

        public string Action<T1, T2, T3>(Func<TController, Action<T1, T2, T3>> action, T1 arg1, T2 arg2, T3 arg3)
        {
            return Action(action, arg1, arg2, arg3, null);
        }

        public string Action<T1, T2, T3>(Func<TController, Action<T1, T2, T3>> action, T1 arg1, T2 arg2, T3 arg3, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3 }, routeValues);
        }

        #endregion

        #region 4 arguments

        public string Action<T1, T2, T3, T4>(Func<TController, Func<T1, T2, T3, T4, ActionResult>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return Action(action, arg1, arg2, arg3, arg4, null);
        }

        public string Action<T1, T2, T3, T4>(Func<TController, Func<T1, T2, T3, T4, ActionResult>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3, arg4 }, routeValues);
        }

        public string Action<T1, T2, T3, T4>(Func<TController, Action<T1, T2, T3, T4>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return Action(action, arg1, arg2, arg3, arg4, null);
        }

        public string Action<T1, T2, T3, T4>(Func<TController, Action<T1, T2, T3, T4>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3, arg4 }, routeValues);
        }

        #endregion
    }
}
